<?php
declare(strict_types=1);

namespace app\service;

use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\MediaUtil;
use WeChatPay\Util\PemUtil;
use think\facade\Config;
use think\facade\Log;

/**
 * 微信服务商请求服务类
 * 优化版本 - 提供更好的错误处理、代码结构和性能
 */
class IspService
{
    // 支持的HTTP方法 MEDIA 为媒体文件
    private const SUPPORTED_METHODS = ['GET', 'POST', 'PATCH', 'MEDIA'];

    private $client;
    private $config;
    private $merchantId;
    private $serialNo;
    private $platformPublicKeyId;
    private $platformPublicKeyInstance;
    private $privateKeyInstance;

    /**
     * 构造函数
     * @param string $merchantTags 商户配置标签
     * @throws \Exception
     */
    public function __construct(string $merchantTags = 'isp')
    {
        $this->initializeConfig($merchantTags);
        $this->initializeKeys();
        $this->initializeClient();
    }

    /**
     * 初始化配置
     * @param string $merchantTags
     * @throws \Exception
     */
    private function initializeConfig(string $merchantTags): void
    {
        $this->config = Config::get($merchantTags);
        if (empty($this->config)) {
            throw new \Exception("配置 '{$merchantTags}' 不存在");
        }
        // 验证必要的配置项
        $requiredKeys = ['mchid', 'serial_no', 'merchantPrivateKey', 'platformPublicKey', 'publicId'];
        foreach ($requiredKeys as $key) {
            if (empty($this->config[$key])) {
                throw new \Exception("配置项 '{$key}' 不能为空");
            }
        }
        $this->merchantId = $this->config['mchid'];
        $this->serialNo = $this->config['serial_no'];
        $this->platformPublicKeyId = $this->config['publicId'];
//        logs('当前服务商配置', $this->config);
    }

    /**
     * 初始化密钥
     * @throws \Exception
     */
    private function initializeKeys(): void
    {
        try {
            // 加载商户私钥
            $merchantPrivateKeyContent = PemUtil::loadPrivateKey($this->config['merchantPrivateKey']);
            $this->privateKeyInstance = Rsa::from($merchantPrivateKeyContent, Rsa::KEY_TYPE_PRIVATE);

            // 加载微信支付公钥
            $platformPublicKeyContent = file_get_contents($this->config['platformPublicKey']);
            if ($platformPublicKeyContent === false) {
                throw new \Exception('无法读取微信支付公钥文件');
            }
            $this->platformPublicKeyInstance = Rsa::from($platformPublicKeyContent, Rsa::KEY_TYPE_PUBLIC);
        } catch (\Exception $e) {
            throw new \Exception('密钥初始化失败: ' . $e->getMessage());
        }
    }

    /**
     * 初始化微信支付客户端
     */
    private function initializeClient(): void
    {
        $this->client = Builder::factory([
            'mchid' => $this->merchantId,
            'serial' => $this->serialNo,
            'privateKey' => $this->privateKeyInstance,
            'certs' => [
                $this->platformPublicKeyId => $this->platformPublicKeyInstance,
            ],
        ]);
    }

    /**
     * 获取配置信息
     * @return array
     */
    public function getConfig(): array
    {
        return $this->config;
    }

    /**
     * 获取商户ID
     * @return string
     */
    public function getMerchantId(): string
    {
        return $this->merchantId;
    }

    /**
     * 获取证书序列号
     * @return string
     */
    public function getSerialNo(): string
    {
        return $this->serialNo;
    }

    /**
     * 加密敏感信息
     * @param string $message 待加密的消息
     * @return string 加密后的字符串
     * @throws \Exception
     */
    public function encryptCrypto(string $message): string
    {
        try {
            return Rsa::encrypt($message, $this->platformPublicKeyInstance);
        } catch (\Exception $e) {
            throw new \Exception('加密失败: ' . $e->getMessage());
        }
    }

    /**
     * 解密信息
     * @param string $encryptedMessage 加密的消息
     * @return string 解密后的字符串
     * @throws \Exception
     */
    public function decryptCrypto(string $encryptedMessage): string
    {
        try {
            return Rsa::decrypt($encryptedMessage, $this->privateKeyInstance);
        } catch (\Exception $e) {
            throw new \Exception('解密失败: ' . $e->getMessage());
        }
    }

    /**
     * 发送请求到微信支付API
     * @param string $url API路径
     * @param array $params 请求参数
     * @param string $method HTTP方法
     * @return array 响应数据
     * @throws \Exception
     */
    public function request(string $url, array $params = [], string $method = 'POST'): array
    {
        $method = strtoupper($method);
        if (!in_array($method, self::SUPPORTED_METHODS)) {
            throw new \Exception("不支持的请求方式: {$method}");
        }
        $options = $this->buildRequestOptions($method, $params);
        try {
            $response = $this->executeRequest($url, $options, $method);
            $responseData = json_decode($response->getBody()->getContents(), true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                throw new \Exception('响应数据JSON解析失败: ' . json_last_error_msg());
            }
            logs('微信支付API响应', ['url' => $url, 'method' => $method, 'response' => $responseData]);
            return $responseData ?? [];
        } catch (\GuzzleHttp\Exception\RequestException $e) {
            return $this->handleRequestException($e);
        } catch (\Exception $e) {
            logs('微信支付API请求异常', ['url' => $url, 'method' => $method, 'error' => $e->getMessage()]);
            throw $e;
        }
    }

    /**
     * 构建请求选项
     * @param string $method
     * @param array $params
     * @return array
     */
    private function buildRequestOptions(string $method, array $params): array
    {
        $options = [
            'headers' => [
                'Accept' => 'application/json',
                'Wechatpay-Serial' => $this->platformPublicKeyId,
            ]
        ];
        if (in_array($method, ['POST', 'PATCH', 'MEDIA'])) {
            $options['json'] = $params;
        }
        if (in_array($method, ['GET'])){
            $options = array_merge($options, $params);
        }
        return $options;
    }

    /**
     * 执行HTTP请求
     * @param string $url
     * @param array $options
     * @param string $method
     * @return \Psr\Http\Message\ResponseInterface
     * @throws \Exception
     */
    private function executeRequest(string $url, array $options, string $method)
    {
        $chain = $this->client->chain(ltrim($url, '/'));
        switch ($method) {
            case 'GET':
                return $chain->get($options);
            case 'POST':
                return $chain->post($options);
            case 'PATCH':
                return $chain->patch($options);
            case 'MEDIA':
                return $this->handleMediaUpload($chain, $options);
            default:
                throw new \Exception("未实现的请求方法: {$method}");
        }
    }

    /**
     * 处理媒体文件上传
     * @param $chain
     * @param array $options
     * @return mixed
     * @throws \Exception
     */
    private function handleMediaUpload($chain, array $options)
    {
        $mediaUrl = $options['json']['media_url'] ?? '';
        if (empty($mediaUrl)) {
            throw new \Exception('媒体文件路径不能为空');
        }
        $media = new MediaUtil($mediaUrl);
        return $chain->post([
            'body' => $media->getStream(),
            'headers' => [
                'Content-Type' => $media->getContentType(),
            ]
        ]);
    }

    /**
     * 处理请求异常
     * @param \GuzzleHttp\Exception\RequestException $e
     * @return array
     * @throws \Exception
     */
    private function handleRequestException(\GuzzleHttp\Exception\RequestException $e): array
    {
        if (!$e->hasResponse()) {
            throw new \Exception('网络请求失败: ' . $e->getMessage());
        }

        $responseBody = $e->getResponse()->getBody()->getContents();
        $errorData = json_decode($responseBody, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new \Exception('请求失败，响应格式错误');
        }

        $errorMessage = $errorData['message'] ?? $errorData['detail'] ?? '未知错误';
        $errorCode = $errorData['code'] ?? 'UNKNOWN_ERROR';

        logs('微信支付API请求失败', [
            'error_code' => $errorCode,
            'error_message' => $errorMessage,
            'response_body' => $responseBody
        ], 'error');
        throw new \Exception("微信支付API错误: {$errorMessage} (错误码: {$errorCode})");
    }

    /**
     * 批量请求（可选的扩展功能）
     * @param array $requests 请求列表 [['url' => '', 'params' => [], 'method' => '']]
     * @return array
     */
    public function batchRequest(array $requests): array
    {
        $results = [];

        foreach ($requests as $index => $request) {
            try {
                $results[$index] = $this->request(
                    $request['url'] ?? '',
                    $request['params'] ?? [],
                    $request['method'] ?? 'POST'
                );
            } catch (\Exception $e) {
                $results[$index] = [
                    'error' => true,
                    'message' => $e->getMessage()
                ];
            }
        }

        return $results;
    }
}
